//
//  MFAExceptionReporter.m
//  NewMissFresh
//
//  Created by 董宝君 on 2019/6/19.
//  Copyright © 2019 com.missfresh. All rights reserved.
//

#import "MFAExceptionReporter.h"
#import "MFAConstants.h"
#import "MFAExceptionNetwork.h"
#import "MFAExceptionQueue.h"
#import "MFALogger.h"
#import "MFAReachability.h"
#import "MFAnalytics.h"
#import "MFAJSONUtility.h"
#import "MFACommonUtility.h"

@interface MFAExceptionReporter ()
@property (nonatomic, strong) dispatch_queue_t serialQueue;
@property (nonatomic, assign) MFAnalyticsNetworkType networkPolicy;
@property (nonatomic, strong) MFAExceptionNetwork *network;
@property (nonatomic, strong) MFAExceptionQueue *messageQueue;
@property (nonatomic, assign) BOOL flushBeforeEnterBackground;
@end

@implementation MFAExceptionReporter

static MFAExceptionReporter *sharedReporter = nil;

+ (MFAExceptionReporter *)sharedReporter {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedReporter = [[MFAExceptionReporter alloc] init];
    });
    return sharedReporter;
}

- (instancetype)init {
    if(self = [super init]){
        //默认 3G、4G、WIFI环境上报数据
        _networkPolicy = MFAnalyticsNetworkType3G | MFAnalyticsNetworkType4G | MFAnalyticsNetworkTypeWIFI;
        
        _flushBeforeEnterBackground = YES;
        
        //串行队列
        _serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"com.missfresh.analytics.exception.reporter.serialQueue.%p",self] UTF8String], DISPATCH_QUEUE_SERIAL);
        
        //通过sqlite创建的异常消息队列v1版本
        _messageQueue = [[MFAExceptionQueue alloc] initWithFilePath:[MFACommonUtility filePathWithName:@"exception-v1"]];
        if(!_messageQueue){
            MFADebug(@"SqliteException: init Exception Queue in Sqlite fail");
        }
        
        //网络
        _network = [[MFAExceptionNetwork alloc] initWithServerURL:[NSURL URLWithString:@"https://monitor-api.missfresh.cn/v1/api"]];
        
        //设置监听者
        [self setupListeners];
    }
    return self;
}

#pragma mark - public

- (void)reportMessage:(nonnull NSString *)message pageName:(nonnull NSString *)pageName {
    if(message.length == 0 || pageName.length == 0){
        return;
    }
    
    dispatch_async(self.serialQueue, ^{
       //组装数据
        NSMutableDictionary *reportDict = [NSMutableDictionary dictionary];
        [reportDict setObject:@"mf_analytics_ios" forKey:@"projectName"];
        [reportDict setObject:@"err_mf_analytics_ios" forKey:@"error_type"];
        [reportDict setObject:message forKey:@"message"];
        [reportDict setObject:@"iOS" forKey:@"platform"];

        NSMutableDictionary *envDict = [NSMutableDictionary dictionary];
        [envDict setObject:pageName forKey:@"page_url"];
        [envDict setObject:[MFACommonUtility networkTypeName] forKey:@"network"];
        [envDict setObject:MF_ANALYTICS_SDK_VERSION forKey:@"sdk_version"];
        [envDict setObject:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] forKey:@"app_version"];
        
        [reportDict setObject:[envDict copy] forKey:@"env"];
        
        //转换成json字符串
        NSString *messageString = [MFAJSONUtility convertObjectToJSONString:[reportDict copy]];
        if(!messageString.length){
            return;
        }
        
        //入队消息
        [self enqueueMessage:messageString];
        
        //如果达到了规定的消息条数后触发上报
        if([self.messageQueue messageCount] >= 1){
            [self flush];
        }
    });
}

#pragma mark - private

- (void)enqueueMessage:(NSString *)message {
    [self.messageQueue addMessage:message type:@"POST"];
}

/**
 上报
 */
- (void)flush {
    dispatch_async(self.serialQueue, ^{
        [self flushVacuumWhenAfterFlushing:NO];
    });
}

/**
 上报数据，在上报完成后vacuum SQLite数据库
 
 @param vacuumAfterFlushing 上报完成后是否优化整理数据库存储空间
 */
- (void)flushVacuumWhenAfterFlushing:(BOOL)vacuumAfterFlushing {
    //判断上报副武器的地址
    if(!self.network.serverURL){
        return;
    }
    //判断允许上报的网络环境
    if(!([MFACommonUtility networkType] & self.networkPolicy)){
        return;
    }
    
    //上报数据
    [self flushWithType:@"POST" flushSize:1];
    
    //上报完成后是否优化数据库存储
    if(vacuumAfterFlushing){
        if(![self.messageQueue vacuum]){
            MFAError(@"failed to VACUUM SQLite.");
        }
    }
}

/**
 根据类型和条数上报数据 （类型目前只有一种POST，保留字段后续会使用）
 
 @param type 消息类型
 @param flushSize 上报的数据大小
 */
- (void)flushWithType:(NSString *)type flushSize:(NSInteger)flushSize {
    //从消息队列头取出一定条数的消息
    NSArray <NSString *> *messageList = [self.messageQueue loadMessageFromHeadWithMessageSize:flushSize type:type];
    if(!messageList){
        MFAError(@"Failed to get records from SQLite.");
        return;
    }
    
    //上传获取到的记录。如果数据上传完成，结束递归
    if(messageList.count == 0 || [messageList firstObject].length == 0 || ![self.network reportException:[messageList firstObject]]){
        return;
    }
    
    //删除已上传的记录。删除失败，结束递归
    if(![self.messageQueue removeMessageFromHeadWithMessageSize:messageList.count type:type]){
        MFAError(@"Failed to remove records from SQLite.");
        return;
    }
    
    //继续上传剩余数据
    [self flushWithType:type flushSize:flushSize];
}

#pragma mark - UIApplication Events

- (void)setupListeners {
    // 监听 App 启动或结束事件
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    
    [notificationCenter addObserver:self
                           selector:@selector(applicationDidEnterBackground:)
                               name:UIApplicationDidEnterBackgroundNotification
                             object:nil];
}

- (void)applicationDidEnterBackground:(NSNotification *)notification {
    
    UIApplication *application = UIApplication.sharedApplication;
    __block UIBackgroundTaskIdentifier backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    // 结束后台任务
    void (^endBackgroundTask)(void) = ^() {
        [application endBackgroundTask:backgroundTaskIdentifier];
        backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    };
    
    backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
        endBackgroundTask();
    }];
    
    if (self.flushBeforeEnterBackground) {
        dispatch_async(self.serialQueue, ^{
            [self flushVacuumWhenAfterFlushing:YES];
            endBackgroundTask();
        });
    } else {
        dispatch_async(self.serialQueue, ^{
            endBackgroundTask();
        });
    }
}

@end
